// vscode安装code runner插件，下载完右键有运行的选项，就可直接运行js代码

// ======================== 原型链继承 ========================
// 缺点：所有实例对象上的属性值都发生了变化
function prototypeDemo() {
  // 原理：当我们创建 Child 类的实例 child1 并给 names 添加数据时，由于 child1 对象自身没有 names 方法，
  // JavaScript引擎会沿着原型链查找，最终在父类的原型上找到了该方法并执行。
  function Parent() {
    this.names = ['kevin', 'daisy']
  }
  Parent.prototype.getName = function () {
    return this.names
  }

  function Child() {
    this.type = 'Child'
  }
  // 重写Child原型对象； 将子类的原型指向父类的实例
  Child.prototype = new Parent()
  // Child实例上没有constructor，需要手动挂上构造器，指向自己的构造函数
  Child.prototype.constructor = Child

  let child1 = new Child()
  let child2 = new Child()

  child1.names.push('铁锤妹妹')
  // console.log(child1, 'child1')
  //  所有实例对象上的属性值都发生了变化
  console.log(child1.names, 'child1') // ['kevin', 'daisy', '铁锤妹妹']
  console.log(child2.names, 'child2') // ['kevin', 'daisy', '铁锤妹妹']
}
prototypeDemo()

// ======================== 构造函数继承 ========================
// 缺点：不能访问父级原型链上的属性和方法

function ConstrutorDemo() {
  function Parent() {
    this.names = ['kevin', 'daisy']
    this.age = 18
  }

  Parent.prototype.getAge = function () {
    return this.age
  }

  function Child(type) {
    // 调用父类的构造函数以继承属性
    Parent.call(this)
    this.type = type
  }

  let child1 = new Child('child')
  let child2 = new Child()

  child1.names.push('Lucy')

  console.log(child1.names) // ["kevin", "daisy", "Lucy"]
  console.log(child2.names) // ["kevin", "daisy"]
  // console.log(child1.getAge()) // 会报错 child1.getAge is not a function
}

ConstrutorDemo()

// ======================== 将原型链和构造函数组合继承 ========================
// 之前的问题都得以解决。但是这里又徒增了一个新问题，那就是Parent3的构造函数会多执行了一次
function Parent3() {
  this.names = ['kevin', 'daisy']
  this.age = 18
}
Parent3.prototype.getAge = function () {
  return this.age
}

function Child3() {
  Parent3.call(this)
  this.type = 'child3'
}
Child3.prototype = new Parent3()
Child3.prototype.constructor = Child3 //手动挂上构造器，指向自己的构造函数

var s3 = new Child3()
var s4 = new Child3()
s3.names.push('铁锤妹妹')
console.log(s3.names, '组合继承s3')
console.log(s4.names, '组合继承s4')
console.log('child3 getAge', s3.getAge())

// ======================== 进一步优化组合继承 - 寄生组合继承 ========================
// 将父类原型对象直接给到子类，父类构造函数只执行一次，而且父类属性和方法均能访问，所以一步到位直接使用寄生组合继承吧
// Object.create() 方法会创建一个新对象，并将该对象的原型设置为传入的参数对象，然后将该对象返回。
function Parent4() {
  this.names = ['kevin', 'daisy']
  this.age = 18
}
Parent4.prototype.getAge = function () {
  return this.age
}
function Child4() {
  Parent4.call(this)
  this.type = 'child4'
}
Child4.prototype = Object.create(Parent4.prototype)
Child4.prototype.constructor = Child4

var group3 = new Child4()
var group4 = new Child4()
group3.names.push('铁锤妹妹')
console.log(group3.names, '寄生组合继承3')
console.log(group4.names, '寄生组合继承4')
console.log('child4 getAge', group3.getAge())

// ================================= obj.hasOwnProperty =================================
function HasOwnPropertyFun(name) {
  this.hasName = name
}

let p1 = new HasOwnPropertyFun('John')
console.log(p1, 'p1')

console.log(p1.hasOwnProperty('hasName')) // true
console.log(p1.hasOwnProperty('toString')) // false  而toString属性是从Object.prototype原型链上继承来的属性。
